package cn.s1995.sloth.pay.biz.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.s1995.sloth.bizs.annotation.RedisLock;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.constant.RedisKeyCst;
import cn.s1995.sloth.common.core.constant.enums.hint.Hint;
import cn.s1995.sloth.bizs.entity.Order;
import cn.s1995.sloth.bizs.entity.OrderRefund;
import cn.s1995.sloth.bizs.entity.PayFlow;
import cn.s1995.sloth.bizs.exception.BizException;
import cn.s1995.sloth.bizs.oo.req.OrderRefundVerifyREQ;
import cn.s1995.sloth.bizs.service.OrderRefundService;
import cn.s1995.sloth.bizs.service.OrderService;
import cn.s1995.sloth.bizs.service.PayFlowService;
import cn.s1995.sloth.common.sequence.sequence.Sequence;
import cn.s1995.sloth.pay.biz.oo.req.PayParamREQ;
import cn.s1995.sloth.pay.biz.oo.vo.PayParamVO;
import cn.s1995.sloth.pay.biz.plugin.paypal.PaypalApiPlus;
import cn.s1995.sloth.pay.biz.service.BizPayService;
import cn.s1995.sloth.pay.entity.PayChannel;
import cn.s1995.sloth.pay.service.PayChannelService;
import cn.s1995.sloth.pay.utils.ChannelPayApiConfigKit;
import cn.s1995.sloth.pay.utils.PayChannelNameEnum;
import cn.s1995.sloth.pay.utils.PayConstants;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.paypal.PayPalApiConfigKit;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * BizPayServiceImpl
 *
 * @author Lex
 * @description
 * @date 2024/01/08 17:34
 */
@Slf4j
@Component
@AllArgsConstructor
public class BizPayServiceImpl implements BizPayService {

    private final OrderService orderService;
    private final PayFlowService payFlowService;
    private final OrderRefundService orderRefundService;
    private final PayChannelService payChannelService;
    private final HttpServletRequest request;
    private final Sequence paySequence;

    @Override
    public void before(Integer payMethod) {
        // BizPayService.super.before(payMethod);
        PayChannel channel = payChannelService.getChannel(PayChannelNameEnum.getChannel(payMethod).getName());
        if (channel == null) {
            throw new BizException(Hint.NON_PAY_CHANNEL_NOT_SUPPORT);
        }
        // 初始化Paypal配置
        PaypalApiPlus.initPaypalConfig(channel);
        ChannelPayApiConfigKit.put(channel);
    }

    @Override
    public void after() {
        // BizPayService.super.after();
        // 移除当前线程缓存的支付参数
        ChannelPayApiConfigKit.remove();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedisLock(name = RedisKeyCst.ORDER_CREATE, key = "#req.uid", expire = 60)
    public PayParamVO createOrder(PayParamREQ req) {
        // 支付-前置处理
        before(req.getPayMethod());

        List<Order> orderList = orderService.getOrderAndItemsList(req.getOrders());

        orderList.stream().filter(item -> item.getIsPayed()).findAny().ifPresent(item -> {
            throw new BizException(Hint.APP_ORDER_PAID);
        });

        // 判断是否已经创建过支付订单
        PayParamVO existsPayParamVO = getPayParamVO(orderList);
        if (existsPayParamVO != null) {
            return existsPayParamVO;
        }

        BigDecimal totalAmount = NumberUtil.add(orderList.stream().map(Order::getActualAmount).toArray(BigDecimal[]::new));
        // 生成支付流水id
        String payFlowId = paySequence.nextNo();
        PayParamVO vo = new PayParamVO();
        vo.setPayFlowId(payFlowId);
        vo.setPayAmount(totalAmount);
        vo.setPayState(BizCst.PayFlow.State.unpay);

        IJPayHttpResponse httpResponse = PaypalApiPlus.createOrder(PayPalApiConfigKit.getApiConfig(), orderList);
        vo.setBody(JSONUtil.parseObj(httpResponse.getBody()));
        if (HttpServletResponse.SC_CREATED == httpResponse.getStatus()) {
            JSONObject bodyObject = JSONUtil.parseObj(httpResponse.getBody());
            // Paypal订单号
            String transactionId = bodyObject.get("id").toString();
            JSONArray links = bodyObject.getJSONArray("links");
            for (int i = 0; i < links.size(); i++) {
                JSONObject item = links.getJSONObject(i);
                String rel = item.getStr("rel");
                String href = item.getStr("href");
                if ("approve".equalsIgnoreCase(rel)) {
                    log.info("Paypal createOrder 支付链接 - {}", href);
                    vo.setPayLink(href);
                    break;
                }
            }

            // 创建支付流水
            createPayFlow(payFlowId, req.getUid(), transactionId, totalAmount, vo.getPayLink());

            // 更新订单支付信息
            orderService.updateOrderPayFlowId(orderList, payFlowId, transactionId, req.getPayMethod());

        } else {
            JSONObject entries = JSONUtil.parseObj(httpResponse.getBody());
            log.error("Paypal createOrder errorCode: {}", httpResponse.getStatus());
            for (Object details : entries.getJSONArray("details")) {
                JSONObject obj = (JSONObject) details;
                log.error("issue -> {}, description -> {}", obj.get("issue"), obj.get("description"));
            }

            throw new BizException(Hint.NON_PAY_FAILED);
        }


        // 支付-后置处理
        after();

        return vo;
    }

    @Override
    public PayParamVO captureOrder(String payFlowId, Boolean... isNeedPrepareHandler) {
        // 根据支付单号获取第三方交易id
        PayFlow payFlow = payFlowService.getPayFlow(payFlowId);
        if (payFlow == null) {
            throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }

        // 预处理标识
        boolean prepareHandlerFlag = !(ArrayUtil.isNotEmpty(isNeedPrepareHandler) && BooleanUtil.isFalse(isNeedPrepareHandler[0]));
        // 判断是否需要预处理
        if (prepareHandlerFlag) {
            // 支付-前置处理
            before(payFlow.getPayMethod());
        }

        // 发起捕获订单
        IJPayHttpResponse httpResponse = PaypalApiPlus.captureOrder(PayPalApiConfigKit.getApiConfig(), payFlow.getTransactionId(), null);
        log.info("captureOrder response - {}", JSONUtil.toJsonStr(httpResponse));
        if (HttpServletResponse.SC_CREATED != httpResponse.getStatus()) {
            JSONObject entries = JSONUtil.parseObj(httpResponse.getBody());
            log.error("Paypal captureOrder errorCode: {}", httpResponse.getStatus());
            for (Object details : entries.getJSONArray("details")) {
                JSONObject obj = (JSONObject) details;
                log.error("issue -> {}, description -> {}", obj.get("issue"), obj.get("description"));
            }

            throw new BizException(Hint.NON_PAY_FAILED);
        }

        if (prepareHandlerFlag) {
            // 支付-后置处理
            after();
        }

        return PayParamVO.builder()
                .payFlowId(payFlowId)
                .payAmount(payFlow.getPayAmount())
                .payState(payFlow.getPayState())
                .body(JSONUtil.parseObj(httpResponse.getBody()))
                .build();
    }

    @Override
    public PayParamVO queryOrder(String payFlowId) {
        // 根据支付单号获取第三方交易id
        PayFlow payFlow = payFlowService.getPayFlow(payFlowId);
        if (payFlow == null) {
            throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }
        // 支付-前置处理
        before(payFlow.getPayMethod());

        IJPayHttpResponse httpResponse = PaypalApiPlus.queryOrder(PayPalApiConfigKit.getApiConfig(), payFlow.getTransactionId());

        after();
        return PayParamVO.builder()
                .payFlowId(payFlowId)
                .payAmount(payFlow.getPayAmount())
                .payState(payFlow.getPayState())
                .body(JSONUtil.parseObj(httpResponse.getBody()))
                .build();
    }

    @Override
    public void refund(String refundId, OrderRefundVerifyREQ req) {
        OrderRefund orderRefund = orderRefundService.getById(refundId);
        if (Objects.isNull(orderRefund)) {
            throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }
        //退款业务逻辑处理
        if (!(orderRefund.getState() == BizCst.OrderRefund.State.refunding) || !(orderRefund.getProcessState() == BizCst.CommonAuditState.toBeAudit)) {
			//数据状态不正确
        	throw new BizException(Hint.FORBIDDEN);
		}
        if (Boolean.TRUE.equals(req.getIsApprove())) {
        	orderRefundService.update(
        			Wrappers.lambdaUpdate(OrderRefund.class)
        					.set(OrderRefund::getProcessState, BizCst.CommonAuditState.approve)
        					.set(OrderRefund::getApprovedTime, LocalDateTime.now())
        					.eq(OrderRefund::getId, refundId)
        	);
	  } else {
		  	//审核不通过
	      	orderRefundService.update(
	    			Wrappers.lambdaUpdate(OrderRefund.class)
	    					.set(OrderRefund::getProcessState, BizCst.CommonAuditState.reject)
	    					.set(OrderRefund::getRejectTime, LocalDateTime.now())
	    					.set(StrUtil.isNotBlank(req.getRejectReason()), OrderRefund::getRejectReason, req.getRejectReason())
	    					.set(OrderRefund::getState, BizCst.OrderRefund.State.failure)
	    					.eq(OrderRefund::getId, refundId)
	    	);
	      	return;
	  }
        // 支付-前置处理
        before(orderRefund.getPayMethod());

        IJPayHttpResponse httpResponse = PaypalApiPlus.refund(PayPalApiConfigKit.getApiConfig(), orderRefund.getId(), orderRefund.getOutRefundNo(), orderRefund.getAmount(), orderRefund.getOrderId());

        if (HttpServletResponse.SC_CREATED != httpResponse.getStatus()) {
            JSONObject entries = JSONUtil.parseObj(httpResponse.getBody());
            log.error("Paypal退款失败 errorCode: {}", httpResponse.getStatus());
            for (Object details : entries.getJSONArray("details")) {
                JSONObject obj = (JSONObject) details;
                log.error("issue -> {}, description -> {}", obj.get("issue"), obj.get("description"));
            }

            throw new BizException(Hint.NON_REFUND_FAILED);
        }

        after();
    }

    /**
     * 获取支付参数
     * @param orderList
     * @return
     */
    @Nullable
    private PayParamVO getPayParamVO(List<Order> orderList) {
        Optional<Order> orderOptional = orderList.stream().filter(item -> Objects.nonNull(item.getTransactionId())).findAny();
        if (orderOptional.isPresent()) {
            PayFlow payFlow = payFlowService.getPayFlowByTransactionId(orderOptional.get().getTransactionId());
            if (payFlow == null) {
                throw new BizException(Hint.APP_ORDER_NOT_EXIST);
            }
            if (Objects.equals(payFlow.getPayState(), BizCst.PayFlow.State.paid)) {
                throw new BizException(Hint.APP_ORDER_PAID);
            }
            // 查询paypal订单
            IJPayHttpResponse response = PaypalApiPlus.queryOrder(PayPalApiConfigKit.getApiConfig(), payFlow.getTransactionId());
            return PayParamVO.builder()
                    .payFlowId(payFlow.getFlowNo())
                    .payAmount(payFlow.getPayAmount())
                    .payState(payFlow.getPayState())
                    .payLink(payFlow.getParam1())
                    .body(JSONUtil.toJsonStr(response.getBody()))
                    .build();
        }
        return null;
    }

    /**
     * 创建支付流水
     *
     * @param payFlowId
     * @param uid
     * @param transactionId
     * @param totalAmount
     * @param payLink
     */
    private void createPayFlow(String payFlowId, Long uid, String transactionId, BigDecimal totalAmount, String payLink) {
        payFlowService.save(
                new PayFlow()
                        .setId(Long.valueOf(payFlowId))
                        .setFlowNo(payFlowId.toString())
                        .setTransactionId(transactionId)

                        .setUid(uid)

                        .setPayType(BizCst.PayFlow.Type.order)
                        .setPayMethod(BizCst.PayMethod.paypal)
                        .setPayState(BizCst.PayFlow.State.unpay)
                        .setPayAmount(totalAmount)

                        .setExtra(payLink)
                        .setParam1(payLink)

                        .setChannelId(ChannelPayApiConfigKit.get().getChannelId())
                        .setChannelMchId(ChannelPayApiConfigKit.get().getChannelMchId())
                        .setClientIp(JakartaServletUtil.getClientIP(request))
                        .setCurrency(PayConstants.CURRENCY_USD)
        );
    }


}
